/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algorithms;

import java.util.ArrayList;
import util.Constants;
import mopso.MetricsReport;
import mopso.PSO;
import mopso.Particle;
import mopso.PerformanceMetrics;
import mopso.PlotManager;
import mopso.Problem;
import mopso.Repository;
import util.Turbulence;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.axis.NumberAxis;

/**
 *
 * @author robson
 */
public class M_DNPSO {

    private ArrayList<Particle> swarm;
    private ArrayList<Particle> externalArchive;
    private Problem problem;
    private int nParticles;
    private int nMaxParticlesExternalArchive;
    private int currentIteration;
    private int currentNCallObjectiveFunction;
    private double inertia;
    private int maxPbests;
    private int maxExternalArchive;
    private double standardDeviationMutationGaussian;
    private Particle auxParticleCurrentPbest;
    private Particle auxParticle2;
    private Particle auxParticle3;
    private Repository repositoryOperators;
    private Turbulence turbulenceOperators;
    private PSO psoOperators;
    private PlotManager plotManager;
    private PerformanceMetrics perfomanceMetrics;
    private double hypervolume;
    private double spacing;
    private double maximumSpread;
    private double timeDuration;
    private long startTimeInMs;
    private long elapsedTimeInMs;

    public M_DNPSO(Problem problem) {

        this.repositoryOperators = new Repository();
        this.turbulenceOperators = new Turbulence();
        this.psoOperators = new PSO();
        this.plotManager = new PlotManager();
        this.plotManager.setupPlotExternalArchive();
        this.plotManager.setupPlotSwarm();

        this.plotManager.setDebug(false);
        this.perfomanceMetrics = new PerformanceMetrics();

        this.swarm = new ArrayList<Particle>();
        this.externalArchive = new ArrayList<Particle>();

        this.problem = problem;
        this.nParticles = Constants.SWARMSIZE;
        this.nMaxParticlesExternalArchive = Constants.EXTERNALARCHIVESIZE;
        
        this.inertia = Constants.MAXINERTIA;

        this.standardDeviationMutationGaussian = Constants.INITIALSDMUTATIONGAUSSIAN;

        this.auxParticleCurrentPbest = new Particle(problem);
        this.auxParticle2 = new Particle(problem);
        this.auxParticle3 = new Particle(problem);

        System.out.println("\nTécnica: m-DNPSO");
        System.out.println("Problema: " + problem.getName());

        startTimeInMs = System.currentTimeMillis();

        // Inicializando swarm
        System.out.println("Inicializando o enxame...");

        this.initializeSwarm();

        if (plotManager.isDebug()) {
            System.out.println("Enxame:");
            plotManager.printParticles(swarm, problem);
        }
    }

    public void run() {

        // Calculando fitness das partículas

        System.out.println("Calculando fitness das partículas...");


        for (int i = 0; i < nParticles; i++) {

            swarm.get(i).calculateFitness();
//            swarm.set(i, repositoryOperators.updatePbest_DNPSO(swarm.get(i), problem));
//            swarm.set(i, repositoryOperators.selectLeaderPbest( swarm.get(i) ) );
        }

        if (plotManager.isDebug()) {
            System.out.println("Enxame:");
            plotManager.printParticles(swarm, problem);
        }

        System.out.println("Inicializando líderes...");
        externalArchive = repositoryOperators.initializeExternalArchive(externalArchive, swarm, problem);

        if (plotManager.isDebug()) {
            System.out.println("Tamanho do arquivo externo: " + externalArchive.size());

            System.out.println("Arquivo externo:");
            plotManager.printParticles(externalArchive, problem);
        }

        System.out.println("Qualificando líderes por crowding distance...");
        externalArchive = repositoryOperators.qualityExternalArchive(externalArchive, problem);

        if (plotManager.isDebug()) {
            System.out.println("Arquivo externo ordenado:");
            plotManager.printParticles(externalArchive, problem);
        }

        this.currentNCallObjectiveFunction = 0;
        this.currentIteration = 0;

        while (currentNCallObjectiveFunction < Constants.NCALLOBJECTIVEFUNCTION) {

            if (plotManager.isDebug()) {
                System.out.println("\nIteracao " + (currentIteration + 1) + "\n");

                System.out.println("Operando sobre as partículas...");
            }

//            swarm = turbulenceOperators.turbulence(swarm, Constants.MUTATIONPERCENTUALPARTICLES, Constants.MUTATIONPERCENTUALDIMENSIONS, standardDeviationMutationGaussian, Constants.INITIALSDMUTATIONGAUSSIAN, Constants.FINALSDMUTATIONGAUSSIAN, nIterations, problem);

            for (int j = 0; j < nParticles; j++) {
                Particle particle = swarm.get(j);

                if (plotManager.isDebug()) {
                    System.out.println("Escolhendo o lider social...");
                }

                particle = repositoryOperators.selectLeader_DNPSO(particle, externalArchive, Constants.F1, Constants.F2, Constants.MNEARESTNEIGHBORS);

                if (plotManager.isDebug()) {
                    System.out.println("Escolhendo lider cognitivo...");
                }

                particle = repositoryOperators.selectLeaderPbest(particle);

                if (plotManager.isDebug()) {
                    System.out.println("Atualizando a posição da particula...");
                }

                inertia = psoOperators.updateRandomInertia();

                particle = psoOperators.updatePosition(particle, problem, inertia, Constants.COGNITIONLEARNINGRATE, Constants.SOCIALLEARNINGRATE);


                if (plotManager.isDebug()) {
                    System.out.println("Calculando fitness da particula...");
                }

                particle.calculateFitness();
                currentNCallObjectiveFunction++;

                if ((currentNCallObjectiveFunction % 10000) == 9999) {
                    plotManager.plotExternalArchive(externalArchive, currentNCallObjectiveFunction + 1);
                    plotManager.plotSwarm(swarm, currentNCallObjectiveFunction + 1);
                }

                if (plotManager.isDebug()) {
                    System.out.println("Atualizando lista Pbest...");
                }

                particle = repositoryOperators.updatePbest_DNPSO(particle, problem);

//                particle = repositoryOperators.updatePbest_CDLS(particle, problem);   

//                particle = repositoryOperators.updatePbest_Test(particle, problem);                

//                particle = repositoryOperators.updatePbest(particle, problem);
//
//                particle = repositoryOperators.qualityPbest(particle, problem);
//
//                particle = repositoryOperators.trunkPbest(particle, Constants.NMAXPBESTS );

                if (particle.getPbests().size() > maxPbests) {
                    maxPbests = particle.getPbests().size();
                }
            }

            if (plotManager.isDebug()) {
                System.out.println("Adicionado novos líderes e removendo partículas dominadas do arquivo externo...");
            }

            externalArchive = repositoryOperators.updateExternalArchive(externalArchive, swarm, problem);

            if (plotManager.isDebug()) {
                System.out.println("Tamanho do arquivo externo: " + externalArchive.size());

                System.out.println("Qualificando líderes por crowding distance...");
            }

            externalArchive = repositoryOperators.qualityExternalArchive(externalArchive, problem);

            if (plotManager.isDebug()) {
                System.out.println("Arquivo externo ordenado:");
                plotManager.printParticles(externalArchive, problem);

                System.out.println("Removendo partículas em excesso do arquivo externo...");
            }

            externalArchive = repositoryOperators.trunkExternalArchive(externalArchive, nMaxParticlesExternalArchive);


            if (externalArchive.size() > maxExternalArchive) {
                maxExternalArchive = externalArchive.size();
            }

            if (plotManager.isDebug()) {
                System.out.println("Adicionado novos líderes e removendo partículas dominadas do arquivo externo...");
                System.out.println("Tamanho do arquivo externo: " + externalArchive.size());
            }

//            inertia = psoOperators.updateLinearInertia(inertia, Constants.MAXINERTIA, Constants.MININERTIA, nIterations);

//            inertia = inertia - ((maxInertia - minInertia) / nIterations);


//            System.out.println("Tamanho maximo atingido pela lista de Pbests na Iteracao " + i + ": "+ maxPbests );
//            
//            maxPbests = 0;


//            this.plotExternalArchive( i+1 );
            currentIteration++;
        }

        elapsedTimeInMs = System.currentTimeMillis() - startTimeInMs;

        plotManager.plotExternalArchive(externalArchive, currentNCallObjectiveFunction);
        plotManager.plotSwarm(swarm, currentNCallObjectiveFunction);
        
        plotManager.getJFreeChartExternalArchive().setTitle("m-DNPSO");

        if (plotManager.isDebug()) {
            System.out.println("Soluções finais (arquivo externo):");
            plotManager.printParticles(externalArchive, problem);
        }

        System.out.println("Tamanho maximo atingido pela lista de Pbests: " + maxPbests);
        System.out.println("Tamanho maximo atingido pelo arquivo externo: " + maxExternalArchive);
        System.out.println("Tamanho final do arquivo externo: " + externalArchive.size());

        this.calculateMetrics();
    }

    void calculateMetrics() {
//        generationalDistance = perfomanceMetrics.calculateGenerationalDistance(swarm,getExternalArchive(), problem);
        hypervolume = perfomanceMetrics.calculateHypervolume2d(externalArchive, problem);
//        spread = perfomanceMetrics.calculateSpread(swarm,getExternalArchive(), problem);
        spacing = perfomanceMetrics.calculateSpacing(externalArchive, problem);
        maximumSpread = perfomanceMetrics.calculateMaximumSpread(externalArchive, problem);
        timeDuration = (double) (elapsedTimeInMs / 1000.0);
//        gmean = perfomanceMetrics.calculateGMean(getExternalArchive());

//        System.out.println("Generational Distance: " + generationalDistance);
        System.out.println("Hypervolume: " + hypervolume);
//        System.out.println("Spread: " + spread);
        System.out.println("Spacing: " + spacing);
        System.out.println("Maximum Spread: " + maximumSpread);
        System.out.println("Time Duration (sec.): " + timeDuration);
//        System.out.println("g() mean: " + gmean);
    }

    private void initializeSwarm() {
        for (int i = 0; i < nParticles; i++) {
            swarm.add(new Particle(problem, i));
        }
    }

    public ChartPanel getChartPanelExternalArchive() {
        return plotManager.getChartPanelExternalArchive();
    }

    public void setChartPanelExternalArchive(ChartPanel chartPanelExternalArchive) {
        plotManager.setChartPanelExternalArchive(chartPanelExternalArchive);
    }

    public ChartPanel getChartPanelSwarm() {
        return plotManager.getChartPanelSwarm();
    }

    public void setChartPanelSwarm(ChartPanel chartPanelSwarm) {
        plotManager.setChartPanelSwarm(chartPanelSwarm);
    }
    
    public NumberAxis getNumberAxisExternalArchive() {
        return plotManager.getNumberAxisExternalArchive();
    }     

    public ArrayList<Particle> getExternalArchive() {
        return externalArchive;
    }

    public void setExternalArchive(ArrayList<Particle> externalArchive) {
        this.externalArchive = externalArchive;
    }

    public MetricsReport getMetrics() {
        return new MetricsReport(hypervolume, spacing, maximumSpread, timeDuration);
    }
}
